//
//  Optional+.swift
//  SplendidSwift
//
//  Created by WangYao on 2023/3/14.
//

import Foundation

public func should(_ do: () throws -> Void) -> Error? {
    do {
        try `do`()
        return nil
    } catch let error {
        return error
    }
}

public func trydo(_ do: () throws -> Void) -> Bool {
    do {
        try `do`()
        return true
    } catch {
        return false
    }
}


public extension Optional {

    var isNone: Bool {
        switch self {
        case .none:
            return true
        case .some:
            return false
        }
    }
    
    var isSome: Bool {
        return !isNone
    }
    
    func or(_ default: Wrapped) -> Wrapped {
        return self ?? `default`
    }

    func or(else: @autoclosure () -> Wrapped) -> Wrapped {
        return self ?? `else`()
    }

    func or(else: () -> Wrapped) -> Wrapped {
        return self ?? `else`()
    }

    func or(throw exception: Error) throws -> Wrapped {
        guard let unwrapped = self else { throw exception }
        return unwrapped
    }
    
    func map<T>(_ fn: (Wrapped) throws -> T, default: T) rethrows -> T {
        return try map(fn) ?? `default`
    }

    func map<T>(_ fn: (Wrapped) throws -> T, else: () throws -> T) rethrows -> T {
        return try map(fn) ?? `else`()
    }

    func on(_ optional: Wrapped?) -> Wrapped? {
        guard let `self` = self else { return optional }
        return self
    }
    
    func and<B>(_ optional: B?) -> B? {
        guard self != nil else { return nil }
        return optional
    }

    @discardableResult
    func and<T>(then: (Wrapped) throws -> T?) rethrows -> T? {
        guard let unwrapped = self else { return nil }
        return try then(unwrapped)
    }

    func zip2<A>(with other: Optional<A>) -> (Wrapped, A)? {
        guard let first = self, let second = other else { return nil }
        return (first, second)
    }
    
    func zip3<A, B>(with other: Optional<A>, another: Optional<B>) -> (Wrapped, A, B)? {
        guard let first = self,
            let second = other,
            let third = another else { return nil }
        return (first, second, third)
    }

    func on(some: () throws -> Void) rethrows {
        if self != nil { try some() }
    }
    
    func on(none: () throws -> Void) rethrows {
        if self == nil { try none() }
    }
    
    func filter(_ predicate: (Wrapped) -> Bool) -> Wrapped? {
        guard let unwrapped = self,
            predicate(unwrapped) else { return nil }
        return self
    }
    
    func expect(_ message: String) -> Wrapped {
        guard let value = self else { fatalError(message) }
        return value
    }
}


public extension Optional where Wrapped == Error {

    func or(_ else: (Error) -> Void) {
        guard let error = self else { return }
        `else`(error)
    }
}

